<template>
  <div class="ie-async-select">
    <n-select
      v-model:value="value"
      :loading="isFetching"
      :options="options"
      :placeholder="props.placeholder"
      :multiple="props.multiple"
      :render-tag="renderTag"
      @update:value="handleUpdateValue"
      @focus="handleFocus"
      @search="handleSearch"
      @clear="handleClear"
      filterable
      clearable
      remote
    />
  </div>
</template>

<script setup>
import { ref, computed, reactive, h, resolveComponent, watch } from 'vue'
import { useQuery } from 'villus'
import gql from 'graphql-tag'
import { isNil } from 'lodash-es'

import CloseCircleFilled from '@vicons/antd/CloseCircleFilled'

const props = defineProps({
  value: [String, Number, Array],
  table: {
    type: String,
    require: true
  },
  valueKey: {
    type: String,
    default: 'id'
  },
  labelKey: {
    type: String,
    default: 'content'
  },
  placeholder: {
    type: String,
    default: '请输入'
  },
  multiple: {
    type: Boolean,
    default: false
  },
  fetchonmount: {
    type: Boolean,
    default: false
  },
  searchable: {
    type: Boolean,
    default: false
  }
})
const emit = defineEmits([
  'update:value'
])

const loaded = ref(props.fetchonmount)
const variables = reactive({
  where: {
    [props.labelKey]: {}
  }
})

const value = ref(props.value)
const { isFetching, data, execute, isDone } = useQuery({
  query: gql`
    query ($where: ${props.table}_bool_exp, $orderBy: [${props.table}_order_by!] = { create_at: desc }) {
      ${props.table} (where: $where, order_by: $orderBy) {
        ${props.valueKey}
        ${props.labelKey}
      }
    }
  `,
  variables,
  fetchOnMount: props.fetchonmount || value.value !== null
})

watch(
  () => props.value,
  propsValue => {
    if (!isDone.value) execute()
    value.value = propsValue
  }
)

const options = computed(() => {
  const rawData = data.value || {}
  const items = rawData[props.table] || []
  const options = []

  for (let i = 0; i < items.length; i += 1) {
    options.push({
      label: items[i][props.labelKey],
      value: items[i][props.valueKey]
    })
  }

  return options
})

function handleUpdateValue(evt) {
  emit('update:value', evt)
}

function handleFocus() {
  if (!loaded.value) {
    if (!props.searchable) {
      loaded.value = true
    } else if (
      isNil(value.value) ||
      props.multiple
    ) {
      variables.where[props.labelKey] = {}
    }

    execute()
  }
}

function handleSearch(s) {
  const trimed = String(s || '').trim()

  if (props.searchable) {
    const toWhere = {}

    if (trimed !== '') {
      toWhere._like = `%${trimed}%`
    }

    variables.where[props.labelKey] = toWhere
  }
}

function handleClear() {
  if (props.searchable) {
    variables.where[props.labelKey] = {}
  }
}

function renderTag({ handleClose, option }) {
  return h('span', {
    class: ['ie-async-select--tag']
  }, [
    h('span', {
      class: ['ie-async-select--tag-box']
    }, [
      h('span', {
        class: ['ie-async-select--tag-content']
      }, {
        default: () => option.label
      }),
      h(resolveComponent('n-icon'), {
        class: ['ie-async-select--tag-close'],
        size: 12,
        onClick: handleClose
      }, {
        default: CloseCircleFilled.render
      })
    ])
  ])
}
</script>

<style lang="less">
.ie-async-select {
  width: 100%;
  max-width: 100%;

  &--tag {
    height: 28px;
    padding: 3px 0;
    display: inline-block;

    &-box {
      color: #909399;
      background-color: #f4f4f5;
      height: 22px;
      font-size: 12px;
      line-height: 18px;
      display: flex;
      align-items: center;
      flex-wrap: nowrap;
      padding: 0 7px;
      border-radius: 4px;
    }

    &-content {
      margin-right: 4px;
      cursor: default;
    }

    &-close {
      color: #c0c4cc;
      transition: color .3s var(--bezier);
      cursor: pointer;

      &:hover {
        color: #909399;
      }
    }
  }
}
</style>
